package cn.moon.client;

import cn.moon.client.channel.ClientChannelManager;
import cn.moon.client.handler.ClientHandler;
import cn.moon.common.config.FrameConfig;
import cn.moon.common.constants.Constants;
import cn.moon.protocol.ProxyMessage;
import cn.moon.protocol.ProxyMessageDecoder;
import cn.moon.protocol.ProxyMessageEncoder;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;


@Slf4j
public class ProxyClient {

    private static final int READ_IDLE = 60;

    @Parameter(names = "-bridge", description = "桥端口")
    private static int bridgePort = 9000;

    /**
     * 本地服务端口
     */
    @Parameter(names = "-local", description = "本地服务端口")
    private static int localPort = 8080;

    @Parameter(names = "-host", description = "服务器地址")
    private static String bridgeHost = "localhost";

    private NioEventLoopGroup workerGroup = new NioEventLoopGroup();

    private Bootstrap clientBootstrap;

    public void startClient(String bridgeHost, int bridgePort) throws InterruptedException {

        clientBootstrap = new Bootstrap();
        clientBootstrap.group(workerGroup);
        clientBootstrap.channel(NioSocketChannel.class);
        clientBootstrap.option(ChannelOption.AUTO_READ, true);

        clientBootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast(new IdleStateHandler(READ_IDLE, 0, 0));
                ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(FrameConfig.MAX_FRAME_LENGTH, FrameConfig.LENGTH_FIELD_OFFSET, FrameConfig.LENGTH_FIELD_LENGTH, 0, FrameConfig.LENGTH_FIELD_LENGTH));
                ch.pipeline().addLast(new ProxyMessageDecoder());
                ch.pipeline().addLast(new ProxyMessageEncoder(FrameConfig.LENGTH_FIELD_LENGTH, true, Constants.NPXY_HEADER));
                ch.pipeline().addLast(new ClientHandler(clientBootstrap, bridgeHost, bridgePort, localPort));
            }
        });

        // TODO: 读取配置
        clientBootstrap.connect(bridgeHost, bridgePort)
                .addListener((ChannelFutureListener) future -> {

                    if (future.isSuccess()) {

                        Channel channel = future.channel();

                        ClientChannelManager.setClientChannel(channel);

                        ProxyMessage proxyMessage = new ProxyMessage();

                        proxyMessage.setType(ProxyMessage.CONNECT);

                        channel.writeAndFlush(proxyMessage);

                        log.info("======== 连接成功 ========");
                    }

                });

    }


    public void start(String bridgeHost, int bridgePort, int localPort) throws InterruptedException {
        startClient(bridgeHost, bridgePort);
        ProxyClient.bridgePort = bridgePort;
        ProxyClient.localPort = localPort;
    }

    public void stop() {
        workerGroup.shutdownGracefully();
    }


    public static void main(String[] args) {

        ProxyClient proxyClient = new ProxyClient();

        JCommander.newBuilder().addObject(proxyClient).build().parse(args);

        try {

            proxyClient.start(bridgeHost, bridgePort, localPort);

            log.info("======== 客户端启动成功 ========");

        } catch (InterruptedException e) {
            log.error("启动失败", e);
        }

    }

}
